package edu.columbia.plt.visage;

/*	Class Agent 
 * 
 *  By Visage Team
 *  Agent class contain the basic information of agent. But agent is not aware of 
 *  environment information like cell and matrix.
 * 
 */

import java.awt.Color;
import java.awt.Point;
import java.awt.geom.Line2D;
import java.util.*;

import edu.columbia.plt.visage.interfaces.IAgent;


public class Agent implements IAgent{

	// member variables include:
	static protected int IDGenerator = 0;
	protected int ID;
	protected double x;
	protected double y;
	protected Color color;
	protected boolean track;
	protected String type;
	protected String shape;
	protected int direction;
	protected int size; // ?
	protected double speed;
	protected boolean isDie;
	protected List<Point> trackHistory;
	
	static public interface Shape {
		final static public String CIRCLE = "CIRCLE";
		final static public String SQUARE = "SQUARE";
		final static public String TRIANGLE = "TRIANGLE";
		final static public String CROSS = "CROSS";
		final static public String DIAMOND = "DIAMOND";
	}

	// construction function
	public Agent(){
		
	}
	
	public Agent(String type) {
		// default color, shape?
		this.type = type;
		this.x = 0;
		this.y = 0;
		this.direction = (int) Math.floor(Math.random() * 360);
		this.shape = Agent.Shape.CIRCLE;
		this.color = Color.blue;
		this.track = false;

		this.size = 3;
		this.speed = .5;

		this.ID = IDGenerator++;
		this.isDie = false;
		//this.trackHistory = new ArrayList<Point>();
	}

	/**
	 * @param color
	 * @param direction
	 * @param shape
	 * @param size
	 * @param speed
	 * @param track
	 * @param type
	 * @param x
	 * @param y
	 */
	public Agent(Color color, int direction, String shape, int size,
			double speed, boolean track, String type, double x, double y) {
		super();
		this.color = color;
		this.direction = direction;
		this.shape = shape;
		this.size = size;
		this.speed = speed;
		this.track = false;
		this.type = type;
		this.x = x;
		this.y = y;
		this.isDie = false;
		this.ID = IDGenerator++;
		//this.trackHistory = new ArrayList<Point>();
	}

	public Agent(Agent agent) {
		this.color = agent.color;
		this.direction = agent.direction;
		this.shape = agent.shape;
		this.size = agent.size;
		this.speed = agent.speed;
		this.track = false;
		this.type = agent.type;
		this.x = agent.x;
		this.y = agent.y;
		this.ID = IDGenerator++;
		this.isDie = false;
		//this.trackHistory = new ArrayList<Point>();
	}

	// get funcs
	/* (non-Javadoc)
	 * @see visage.Agents#getID()
	 */
	public int getID() {
		return ID;
	}

	public double getVariable(String variable){
		if(variable.equalsIgnoreCase("x"))
			return getX();
		if(variable.equalsIgnoreCase("y"))
			return getY();
		if(variable.equalsIgnoreCase("direction"))
			return direction;
		if(variable.equalsIgnoreCase("size"))
			return size;
		if(variable.equalsIgnoreCase("speed"))
			return speed;
		else
			return 0;
	}
	/* (non-Javadoc)
	 * @see visage.Agents#getX()
	 */
	public double getX() {
		return x;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#getY()
	 */
	public double getY() {
		return y;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#getColor()
	 */
	public Color getColor() {
		return color;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#getType()
	 */
	public String getType() {
		return type;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#getShape()
	 */
	public String getShape() {
		return shape;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#getDirection()
	 */
	public int getDirection() {
		return direction;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#getSize()
	 */
	public int getSize() {
		return size;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#getSpeed()
	 */
	public double getSpeed() {
		return speed;
	}

	// set funcs
	/* (non-Javadoc)
	 * @see visage.Agents#setID()
	 */
	public void setID() {
		this.ID = IDGenerator++;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#setColor(java.awt.Color)
	 */
	public void setColor(Color color) {
		this.color = color;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#setType(java.lang.String)
	 */
	public void setType(String type) {
		this.type = type;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#setShape(java.lang.String)
	 */
	public void setShape(String shape) {
		this.shape = shape;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#setDirection(int)
	 */
	public void setDirection(int direction) {
		this.direction = direction;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#setSize(int)
	 */
	public void setSize(int size) {
		this.size = size;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#setSpeed(double)
	 */
	public void setSpeed(double speed) {
		this.speed = speed;
	}

	// agent funcs
	// change color
	/* (non-Javadoc)
	 * @see visage.Agents#changeColor(java.awt.Color)
	 */
	public void changeColor(Color color) {
		this.color = color;
	}

	// change type
	/* (non-Javadoc)
	 * @see visage.Agents#become(java.lang.String)
	 */


	// produce <number> agents with type
	/* (non-Javadoc)
	 * @see visage.Agents#produce(int, java.lang.String)
	 */
	public Collection<Agent> produce(int number, String type) {
		Collection<Agent> set = new HashSet<Agent>();
		for (int i = 0; i < number; i++) {
			Agent agent = new Agent(this);
			agent.setType(type);
			set.add(agent);
		}
		return set;
	}

	// reproduce <number> agents
	/* (non-Javadoc)
	 * @see visage.Agents#reproduce(int)
	 */
	public Collection<Agent> reproduce(int number) {
		Collection<Agent> set = new HashSet<Agent>();
		for (int i = 0; i < number; i++) {
			Agent agent = new Agent(this);
			set.add(agent);
		}
		return set;
	}

	// kill itself
	/* (non-Javadoc)
	 * @see visage.Agents#die()
	 */
	public void die() {
		this.freeze();
		this.isDie = true;

	}

	// move in the direction with certain speed
	/* (non-Javadoc)
	 * @see visage.Agents#move()
	 */
	public void move() {
		x += speed * Math.cos(Math.toRadians(direction));
		y += speed * Math.sin(Math.toRadians(direction));
	}

	// move randomly
	/* (non-Javadoc)
	 * @see visage.Agents#moverandom()
	 */
	public void moverandom() {
	//	double tmpSpeed = speed * Math.random();
	//	x += tmpSpeed * Math.cos(Math.toRadians(360 * Math.random()));
	//	y += tmpSpeed * Math.sin(Math.toRadians(360 * Math.random()));
		turnRight((int) (30 - 60 * Math.random()));
		move();
	}

	// grow certain size
	/* (non-Javadoc)
	 * @see visage.Agents#grow(int)
	 */
	public void grow(int size) {
		this.size += size;
	}

	// size++;
	/* (non-Javadoc)
	 * @see visage.Agents#grow()
	 */
	public void grow() {
		this.size++;
	}

	// shrink certain size
	/* (non-Javadoc)
	 * @see visage.Agents#shrink(int)
	 */
	public void shrink(int size) {
		this.size -= size;
		if (this.size < 1)
			this.size = 1;
	}

	// size--;
	/* (non-Javadoc)
	 * @see visage.Agents#shrink()
	 */
	public void shrink() {
		this.size--;
		if (this.size < 1)
			this.size = 1;
	}

	// turn on track
	/* (non-Javadoc)
	 * @see visage.Agents#trackon()
	 */
	public void trackon() {
		if (!track){
			trackHistory = new ArrayList<Point>();
			//System.out.println("trackon!");
		}
		track = true;
	}

	public void appendTrackHistory(double x, double y) {
		Point p = new Point();
		p.setLocation(x*Matrix.getCellSize(), y*Matrix.getCellSize());
		trackHistory.add(p);
	}
	
	public List<Point> getTrackHistory() {
		return trackHistory;
	}
	// turn off track
	/* (non-Javadoc)
	 * @see visage.Agents#trackoff()
	 */
	public void trackoff() {
		track = false;
	}

	// switch track
	/* (non-Javadoc)
	 * @see visage.Agents#tracktoggle()
	 */
	public void tracktoggle() {
		if(!track)
			trackon();
		else
			track = false;
		
	}

	// color brighten
	/* (non-Javadoc)
	 * @see visage.Agents#brighten()
	 */
	public void brighten() {
		color = color.brighter();
	
	}

	// color darken
	/* (non-Javadoc)
	 * @see visage.Agents#darken()
	 */
	public void darken() {
		color = color.darker();
	}

	// add certain speed
	/* (non-Javadoc)
	 * @see visage.Agents#speedUp(double)
	 */
	public void speedUp(double speed) {
		this.speed += speed;
	}

	// speed++;
	/* (non-Javadoc)
	 * @see visage.Agents#speedUp()
	 */
	public void speedUp() {
		if (speed < .05)
			speed = .05;
		else if (speed < 2)
			this.speed *= 1.25;
		else this.speed++;
	}

	// slow certain speed
	/* (non-Javadoc)
	 * @see visage.Agents#slowDown(double)
	 */
	public void slowDown(double speed) {
		this.speed -= speed;
		if (this.speed < 0)
			this.speed = 0;
	}

	// speed--;
	/* (non-Javadoc)
	 * @see visage.Agents#slowDown()
	 */
	public void slowDown() {
		this.speed *= .8;
	}

	// set agent position
	/* (non-Javadoc)
	 * @see visage.Agents#setPosition(double, double)
	 */
	public void setPosition(double x, double y) {
		this.x = x;
		this.y = y;
	}

	// turn right certain degree
	/* (non-Javadoc)
	 * @see visage.Agents#turnRight(int)
	 */
	public void turnRight(int degree) {
		direction -= degree;
		direction = direction % 360;
	}

	// turn right 90 degree
	/* (non-Javadoc)
	 * @see visage.Agents#turnRight()
	 */
	public void turnRight() {
		direction -= 90;
		direction = direction % 360;
	}

	// turn left certain degree
	/* (non-Javadoc)
	 * @see visage.Agents#turnLeft(int)
	 */
	public void turnLeft(int degree) {
		direction += degree;
		direction = direction % 360;
	}

	// turn left 90 degree
	/* (non-Javadoc)
	 * @see visage.Agents#turnLeft()
	 */
	public void turnLeft() {
		direction += 90;
		direction = direction % 360;
	}

	/* (non-Javadoc)
	 * @see visage.Agents#freeze()
	 */
	public void freeze() {
		speed = 0;
	}

	// kill agents
	/* (non-Javadoc)
	 * @see visage.Agents#kill(java.util.Collection)
	 */
	public void kill(Collection<Agent> agents) {
		Iterator<Agent> it = agents.iterator();
		while (it.hasNext()) {
			Agent agent = it.next();
			System.out.println("Agent Type: " + agent.type + ", ID: "
					+ agent.getID() + "killed!");
			agent.freeze();
			agent.die();
			//System.out.println("Number of Agents: "+Matrix.getAgentSet().size(
			// ));
		}
	}

	/* (non-Javadoc)
	 * @see visage.Agents#print()
	 */
	public void print() {
		System.out.println("P" + this.toString());
	}
}
